package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"

	"github.com/jroimartin/gocui"
)

type TreeViewSource interface {
	Text() string
	Icon() string
	Level() int
}

type TreeViewKeyCb func(selection int, key gocui.Key, w *TreeViewWidget)

type TreeViewWidget struct {
	x, y        int
	w, h        int
	top, bottom int
	nodes       []TreeViewSource
	selection   int
	view        *gocui.View
	keyCbs      map[gocui.Key]TreeViewKeyCb
	logFile     *os.File
}

func NewTreeView(x int, y int, w int, h int, sources []TreeViewSource) *TreeViewWidget {
	t := &TreeViewWidget{
		x:         x,
		y:         y,
		w:         w,
		h:         h,
		top:       0,
		bottom:    h - 1,
		selection: 0,
	}
	t.nodes = sources
	t.keyCbs = map[gocui.Key]TreeViewKeyCb{}
	t.logFile, _ = os.OpenFile("/tmp/treeview.log.9", os.O_WRONLY|os.O_APPEND, 0666)
	fmt.Fprintln(t.logFile, w, h)
	return t
}

func (t *TreeViewWidget) Refresh() {
	t.view.Clear()
	fmt.Fprintln(t.logFile, t.top, t.bottom, t.h, t.selection)
	if t.selection > t.bottom {
		fmt.Fprintln(t.logFile, ">=")
		t.bottom = t.selection
		t.top = t.bottom - t.h + 1
	}
	if t.selection < t.top {
		fmt.Fprintln(t.logFile, t.selection, "<=", t.top)
		t.top = t.selection
		t.bottom = t.top + t.h - 1
	}

	for n, node := range t.nodes[t.top:] {
		showText := ""
		showText += strings.Repeat(" ", node.Level()*2)
		showText += node.Icon()
		if t.selection-t.top == n {
			showText += "\033[7m"
		}
		showText += node.Text()
		if t.selection-t.top == n {
			showText += "\033[0m"
		}
		fmt.Fprintln(t.view, showText)
	}
}

func (t *TreeViewWidget) KeyUp(g *gocui.Gui, v *gocui.View) error {
	if t.selection == 0 {
		return nil
	}
	t.selection -= 1
	t.Refresh()
	return nil
}

func (t *TreeViewWidget) KeyDown(g *gocui.Gui, v *gocui.View) error {
	if t.selection >= len(t.nodes)-1 {
		return nil
	}
	t.selection += 1
	t.Refresh()
	return nil
}

func (t *TreeViewWidget) Key(key gocui.Key) {
	if _, ok := t.keyCbs[key]; !ok {
		return
	}
	t.keyCbs[key](t.selection, key, t)
}

func (t *TreeViewWidget) UpdateSource(nodes []TreeViewSource) {
	t.nodes = nodes
	t.Refresh()
}

func (t *TreeViewWidget) SetKeyCb(key gocui.Key, cb TreeViewKeyCb) func(*gocui.Gui, *gocui.View) error {
	t.keyCbs[key] = cb
	return func(g *gocui.Gui, v *gocui.View) error {
		t.Key(key)
		return nil
	}
}

func (t *TreeViewWidget) Layout(g *gocui.Gui) error {
	var err error
	// 这里似乎不用边框也要预留边框的位置
	t.view, err = g.SetView("tree", t.x-1, t.y-1, t.x+t.w+1, t.y+t.h+1)
	t.view.Autoscroll = false
	t.view.Frame = false
	if err != nil && err != gocui.ErrUnknownView {
		return err
	}
	t.Refresh()
	return nil
}

func quit(g *gocui.Gui, v *gocui.View) error {
	return gocui.ErrQuit
}

func KeyEnter(selection int, key gocui.Key, w *TreeViewWidget) {
	if selection < 0 || selection >= len(w.nodes) {
		return
	}
	if w.nodes[selection].(*FileNode)._type == NodeTypeFile {
		cmd := exec.Command("vim", "--servername", servername, "--remote-tab", w.nodes[selection].(*FileNode).path)
		cmd.Run()
		return
	}
	w.UpdateSource(Toggle(w.nodes, selection))
}

func KeyPage(selection int, key gocui.Key, w *TreeViewWidget) {

}

var (
	servername string
	filepath   string
)

func main() {
	// 读取各种参数
	_servername := flag.String("servername", "", "vim server name")
	_filepath := flag.String("path", "./", "filepath")
	flag.Parse()
	if *_servername == "" {
		panic("need server name")
	}
	if *_filepath == "" {
		panic("need path")
	}
	servername = *_servername
	filepath = *_filepath

	g, err := gocui.NewGui(gocui.OutputNormal)
	defer func() {
		g.Close()
	}()
	if err != nil {
		log.Panicln(err)
		return
	}

	rootDir := NewFileNode(filepath)
	viewSource := GetSource(rootDir)

	w, h := g.Size()
	treeView := NewTreeView(0, 0, w, h, viewSource)
	g.SetManager(treeView)

	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
		log.Panicln(err)
	}

	if err := g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, treeView.SetKeyCb(gocui.KeyEnter, KeyEnter)); err != nil {
		log.Panicln(err)
	}

	if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, treeView.KeyUp); err != nil {
		log.Panicln(err)
	}

	if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, treeView.KeyDown); err != nil {
		log.Panicln(err)
	}

	if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
		log.Panicln(err)
		return
	}
}
